#include <sem.h>          // sem_t 的定义的等
#include <linux/sched.h>  // 使用了schedule 函数
#include <linux/kernel.h> // 使用了printk 函数
#include <asm/system.h>   // cli(), sti(), 实现原子操作
#include <asm/segment.h>  // 使用了get_fs_byte 函数

#define NAME_SIZE 32

/* 信号量本质就是一种结构体，所以要消耗内存资源，故是有限的 */
// 用数组来管理
#define SEM_COUNT 32
sem_t semaphores[SEM_COUNT];

/* 判断某信号量的等待队列是否已满 */
// 循环队列：空1个位置，以判断队满
int is_full(sem_queue *q)
{
    if ((q->rear + 1) % QUE_LEN == q->front)
        return 1;
    else
        return 0;
}

/* 进程被阻塞，挂到某个信号量的等待队列中；*/
int insert_task(struct task_struct *p, sem_queue *q)
{
    if (is_full(q))
    {
        printk("Queue is full!\n");
        return -1; // 插入失败
    }
    else
    {
        // 入队操作
        q->wait_tasks[q->rear] = p;
        q->rear = (q->rear + 1) % QUE_LEN;
        return 1; // 插入成功
    }
}

/* 判断某信号量的等待队列是否为空 */
int is_empty(sem_queue *q)
{
    return q->rear == q->front ? 1 : 0; // 1表示为空
}

/* 获得循环队列的第1个任务 */
struct task_struct *get_task(sem_queue *q)
{
    if (is_empty(q))
    {
        printk("Queue is empty!\n");
        return NULL;
    }
    else
    {
        struct task_struct *tmp = q->wait_tasks[q->front];
        q->front = (q->front + 1) % QUE_LEN;
        return tmp;
    }
}

/* 判断信号量是否已经打开了 */
// 信号量的名字是唯一的，所以根据名字来判断
int is_sem_opened(const char *name, int name_size)
{
    int i;
    for (i = 0; i < SEM_COUNT; i++)
    {
        if (semaphores[i].occupied == 1)
        {
            int j;
            int flag = 1;
            // 名字匹配
            for (j = 0; j < name_size; j++)
            {
                if (name[j] != semaphores[i].name[j])
                {
                    flag = 0;
                    break;
                }
            }
            if (flag)
                return i;
        }
    }
    return -1; // 未使用的信号量
}

/* 初始化信号量的等待队列 */
void init_queue(sem_queue *q)
{
    q->front = q->rear = 0;
}

/* 打开(申请)信号量资源 */
sem_t *sys_sem_open(const char *name, unsigned int value)
{
    char sem_name[NAME_SIZE];
    int i;
    for (i = 0; i < NAME_SIZE; i++)
    {
        char c = get_fs_byte(name + i);
        sem_name[i] = c;
        if (c == '\0')
            break;
    }
    if (i == NAME_SIZE)
    {
        printk("The name of semaphore is too long!\n");
        return NULL;
    }
    int id;
    if ((id = is_sem_opened(sem_name, i)) != -1)
    {
        // 已使用的信号量
        return &semaphores[id];
    }

    /* 未使用的信号量 */
    int j;
    for (j = 0; j < SEM_COUNT; j++)
    {
        if (semaphores[j].occupied == 0)
        {
            /* 复制名字 */
            int tmp;
            for (tmp = 0; tmp <= i; tmp++)
            {
                semaphores[j].name[tmp] = sem_name[tmp];
            }

            semaphores[j].occupied = 1;
            semaphores[j].value = value;

            // 初始化该信号量的等待队列
            init_queue(&(semaphores[j].wait_queue));
            return &semaphores[j];
        }
    }

    /* 信号量数组已经满了 */
    printk("Numbers of semaphores are limited!\n");
    return NULL;
}

/* 释放信号量资源 */
int sys_sem_unlink(const char *name)
{
    char sem_name[NAME_SIZE];
    int i;
    for (i = 0; i < NAME_SIZE; i++)
    {
        char c = get_fs_byte(name + i);
        sem_name[i] = c;
        if (c == '\0')
            break;
    }
    if (i == NAME_SIZE)
    {
        printk("The name of semaphore is too long!\n");
        return -1;
    }

    /* 判断是否打开了 */
    int id;
    if ((id = is_sem_opened(sem_name, i)) != -1)
    {
        // 打开了，才释放
        semaphores[id].occupied = 0;
        return 0;
    }
    return -1;
}

/* P 操作 */
int sys_sem_wait(sem_t *sem)
{
    cli();
    sem->value--;
    if (sem->value < 0)
    {
        current->state = TASK_UNINTERRUPTIBLE;
        insert_task(current, &(sem->wait_queue));
        schedule();
    }
    sti();
    return 0;
}

/* V 操作 */
int sys_sem_post(sem_t *sem)
{
    cli();
    sem->value++;
    if (sem->value <= 0)
    {
        struct task_struct *p = get_task(&(sem->wait_queue));
        if (p != NULL)
        {
            p->state = TASK_RUNNING;
        }
    }
    sti();
    return 0;
}
